﻿using System.Net;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace BPM.Web.Shared.Interceptor;

internal class HttpClientInterceptorHandler : HttpMessageHandler
{
    private static readonly MethodInfo? s_sendAsyncMethod = typeof(HttpMessageHandler)
        .GetMethod(nameof(SendAsync), BindingFlags.Instance | BindingFlags.NonPublic);

    private TokenInterceptor? _interceptor;

    private readonly IServiceProvider _services;

    private readonly HttpMessageHandler _baseHandler;

    public HttpClientInterceptorHandler(IServiceProvider services, HttpMessageHandler baseHandler)
    {
        _services = services;
        _baseHandler = baseHandler;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (_interceptor == null)
        {
            _interceptor = _services.GetService<TokenInterceptor>();
        }
        var response = new HttpResponseMessage();
        var args = new HttpClientInterceptorEventArgs(request, response);
        try
        {
            if (_interceptor != null)
            {
                await _interceptor.InvokeBeforeSendAsync(args);
            }
            if (args.Cancel)
            {
                response = new HttpResponseMessage(HttpStatusCode.NoContent);
            }
            else
            {
                if (s_sendAsyncMethod != null)
                {
                    if (s_sendAsyncMethod.Invoke(_baseHandler, new object[] { request, cancellationToken }) is Task<HttpResponseMessage> responseTask)
                    {
                        response = await responseTask;
                    }
                }
            }
            return response;
        }
        finally
        {
            if (!args.Cancel)
            {
                var argsAfter = new HttpClientInterceptorEventArgs(request, response);
                if (_interceptor != null)
                {
                    await _interceptor.InvokeAfterSendAsync(argsAfter);
                }
                await args._asyncTask;
            }
        }
    }

    protected override void Dispose(bool disposing)
    {
        _baseHandler?.Dispose();
        base.Dispose(disposing);
    }
}

